/*
 *    Copyright 2008 Lammert Westerhoff
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.googlecode.maven.plugins.dependencyconflicts;

import java.util.List;
import java.util.Set;

import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
import org.apache.maven.shared.dependency.tree.DependencyNode;
import org.apache.maven.shared.dependency.tree.traversal.DependencyNodeVisitor;

/**
 * An implementation of the {@link DependencyNodeVisitor} that resolves dependency conflicts of the nodes it is
 * visiting.
 * 
 * @author Lammert Westerhoff
 */
public class DependencyConflictsResolverNodeVisitor
    implements DependencyNodeVisitor
{

    private final Set<DependencyNode> errorNodes;

    private final Set<DependencyNode> warningNodes;

    private final Set<DependencyNode> infoNodes;

    /**
     * Construct a <code>DependencyConflictsResolverNodeVisitor</code> with three set to which the conflicting nodes are
     * added. The actual sets are used, no copy is created.
     * 
     * @param errorNodes the error conflicting nodes will be added to this set
     * @param warningNodes the warning conflicting nodes will be added to this set
     * @param infoNodes the info nodes conflicting nodes will be added to this set
     */
    public DependencyConflictsResolverNodeVisitor( final Set<DependencyNode> errorNodes,
                                                   final Set<DependencyNode> warningNodes,
                                                   final Set<DependencyNode> infoNodes )
    {
        super();
        this.errorNodes = errorNodes;
        this.warningNodes = warningNodes;
        this.infoNodes = infoNodes;
    }

    /*
     * (non-Javadoc)
     * @see
     * org.apache.maven.shared.dependency.tree.traversal.DependencyNodeVisitor#endVisit(org.apache.maven.shared.dependency
     * .tree.DependencyNode)
     */
    @Override
    public boolean endVisit( final DependencyNode node )
    {
        return true;
    }

    /*
     * (non-Javadoc)
     * @see
     * org.apache.maven.shared.dependency.tree.traversal.DependencyNodeVisitor#visit(org.apache.maven.shared.dependency
     * .tree.DependencyNode)
     */
    @Override
    public boolean visit( final DependencyNode node )
    {
        if ( node.getState() == DependencyNode.OMITTED_FOR_CONFLICT )
        {
            final Set<DependencyNode> nodeSet;
            final boolean thisGreaterThanRelated =
                new DefaultArtifactVersion( node.getArtifact().getVersion() ).compareTo( new DefaultArtifactVersion(
                                                                                                                     node.getRelatedArtifact().getVersion() ) ) > 0;
            if ( thisGreaterThanRelated )
            {
                nodeSet =
                    isDependencyExplicitlySpecified( node.getParent(), node.getRelatedArtifact() ) ? getWarningNodes()
                                    : getErrorNodes();
            }
            else
            {
                nodeSet = getInfoNodes();
            }
            nodeSet.add( node );
        }
        return true;
    }

    @SuppressWarnings( "unchecked" )
    private boolean isDependencyExplicitlySpecified( final DependencyNode node, final Artifact dependencyArtifact )
    {
        for ( final DependencyNode child : (List<DependencyNode>) node.getChildren() )
        {
            if ( child.getArtifact().equals( dependencyArtifact ) )
            {
                return true;
            }
        }

        return node.getParent() == null ? false
                        : isDependencyExplicitlySpecified( node.getParent(), dependencyArtifact );
    }

    /**
     * Get the set with error conflicting nodes.
     * 
     * @return the errorNodes
     */
    public final Set<DependencyNode> getErrorNodes()
    {
        return this.errorNodes;
    }

    /**
     * Get the set with warning conflicting nodes.
     * 
     * @return the warningNodes
     */
    public final Set<DependencyNode> getWarningNodes()
    {
        return this.warningNodes;
    }

    /**
     * Get the set with info conflicting nodes.
     * 
     * @return the infoNodes
     */
    public final Set<DependencyNode> getInfoNodes()
    {
        return this.infoNodes;
    }
}
